#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Backup Monitor Script
备份监控脚本

功能：
- 监控备份任务的执行状态
- 检查备份文件的完整性
- 发送监控报告和告警
- 记录备份性能指标
"""

import os
import sys
import json
import time
import logging
import subprocess
from datetime import datetime, timedelta
from pathlib import Path

# 配置信息
BACKUP_DIR = "{{ backup_base_dir }}"
LOG_DIR = "{{ backup_log_dir }}"
MONITOR_LOG = os.path.join(LOG_DIR, "monitor.log")
STATUS_FILE = os.path.join(BACKUP_DIR, "backup_status.json")

# 监控配置
MAX_BACKUP_AGE_HOURS = {{ backup_monitoring_config.max_age_hours | default(25) }}
MIN_BACKUP_SIZE_MB = {{ backup_monitoring_config.min_size_mb | default(1) }}
ALERT_EMAIL = "{{ backup_monitoring_config.alert_email | default('') }}"
SMTP_SERVER = "{{ backup_monitoring_config.smtp_server | default('localhost') }}"

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(MONITOR_LOG),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class BackupMonitor:
    """备份监控类"""
    
    def __init__(self):
        self.backup_dir = Path(BACKUP_DIR)
        self.log_dir = Path(LOG_DIR)
        self.status = {
            'last_check': datetime.now().isoformat(),
            'backups': [],
            'alerts': [],
            'summary': {}
        }
    
    def check_backup_files(self):
        """检查备份文件"""
        logger.info("开始检查备份文件...")
        
        if not self.backup_dir.exists():
            logger.error(f"备份目录不存在: {self.backup_dir}")
            return False
        
        backup_files = list(self.backup_dir.glob("*.tar.gz")) + list(self.backup_dir.glob("*.sql"))
        
        for backup_file in backup_files:
            file_info = self.analyze_backup_file(backup_file)
            self.status['backups'].append(file_info)
            
            # 检查文件年龄
            if file_info['age_hours'] > MAX_BACKUP_AGE_HOURS:
                alert = f"备份文件过旧: {backup_file.name} ({file_info['age_hours']:.1f}小时)"
                self.status['alerts'].append(alert)
                logger.warning(alert)
            
            # 检查文件大小
            if file_info['size_mb'] < MIN_BACKUP_SIZE_MB:
                alert = f"备份文件过小: {backup_file.name} ({file_info['size_mb']:.1f}MB)"
                self.status['alerts'].append(alert)
                logger.warning(alert)
        
        logger.info(f"检查完成，共发现 {len(backup_files)} 个备份文件")
        return True
    
    def analyze_backup_file(self, file_path):
        """分析备份文件信息"""
        stat = file_path.stat()
        size_mb = stat.st_size / (1024 * 1024)
        mtime = datetime.fromtimestamp(stat.st_mtime)
        age_hours = (datetime.now() - mtime).total_seconds() / 3600
        
        return {
            'name': file_path.name,
            'path': str(file_path),
            'size_mb': round(size_mb, 2),
            'modified_time': mtime.isoformat(),
            'age_hours': round(age_hours, 2),
            'is_valid': self.verify_backup_integrity(file_path)
        }
    
    def verify_backup_integrity(self, file_path):
        """验证备份文件完整性"""
        try:
            if file_path.suffix == '.gz':
                # 验证gzip文件
                result = subprocess.run(['gzip', '-t', str(file_path)], 
                                      capture_output=True, text=True)
                return result.returncode == 0
            elif file_path.suffix == '.sql':
                # 简单检查SQL文件是否可读
                with open(file_path, 'r') as f:
                    f.read(1024)  # 读取前1KB
                return True
            else:
                return True
        except Exception as e:
            logger.error(f"验证文件完整性失败 {file_path}: {e}")
            return False
    
    def check_backup_processes(self):
        """检查正在运行的备份进程"""
        try:
            result = subprocess.run(['pgrep', '-f', 'backup'], 
                                  capture_output=True, text=True)
            if result.stdout.strip():
                pids = result.stdout.strip().split('\n')
                logger.info(f"发现 {len(pids)} 个备份进程正在运行")
                return len(pids)
            else:
                logger.info("没有发现正在运行的备份进程")
                return 0
        except Exception as e:
            logger.error(f"检查备份进程失败: {e}")
            return -1
    
    def generate_summary(self):
        """生成监控摘要"""
        total_backups = len(self.status['backups'])
        total_alerts = len(self.status['alerts'])
        
        if total_backups > 0:
            total_size = sum(b['size_mb'] for b in self.status['backups'])
            avg_age = sum(b['age_hours'] for b in self.status['backups']) / total_backups
            valid_backups = sum(1 for b in self.status['backups'] if b['is_valid'])
        else:
            total_size = 0
            avg_age = 0
            valid_backups = 0
        
        self.status['summary'] = {
            'total_backups': total_backups,
            'valid_backups': valid_backups,
            'total_size_mb': round(total_size, 2),
            'average_age_hours': round(avg_age, 2),
            'total_alerts': total_alerts,
            'running_processes': self.check_backup_processes()
        }
    
    def save_status(self):
        """保存监控状态"""
        try:
            with open(STATUS_FILE, 'w') as f:
                json.dump(self.status, f, indent=2, ensure_ascii=False)
            logger.info(f"监控状态已保存到: {STATUS_FILE}")
        except Exception as e:
            logger.error(f"保存监控状态失败: {e}")
    
    def send_alerts(self):
        """发送告警"""
        if not self.status['alerts'] or not ALERT_EMAIL:
            return
        
        try:
            import smtplib
            from email.mime.text import MIMEText
            from email.mime.multipart import MIMEMultipart
            
            msg = MIMEMultipart()
            msg['From'] = 'backup-monitor@{{ ansible_hostname }}'
            msg['To'] = ALERT_EMAIL
            msg['Subject'] = f'备份监控告警 - {{ ansible_hostname }}'
            
            body = "\n".join([
                "备份监控发现以下问题：",
                "",
                "\n".join(f"- {alert}" for alert in self.status['alerts']),
                "",
                f"监控时间: {self.status['last_check']}",
                f"服务器: {{ ansible_hostname }}"
            ])
            
            msg.attach(MIMEText(body, 'plain', 'utf-8'))
            
            server = smtplib.SMTP(SMTP_SERVER)
            server.send_message(msg)
            server.quit()
            
            logger.info(f"告警邮件已发送到: {ALERT_EMAIL}")
        except Exception as e:
            logger.error(f"发送告警邮件失败: {e}")
    
    def run(self):
        """运行监控"""
        logger.info("开始备份监控...")
        
        try:
            self.check_backup_files()
            self.generate_summary()
            self.save_status()
            
            if self.status['alerts']:
                self.send_alerts()
            
            # 输出摘要
            summary = self.status['summary']
            logger.info(f"监控完成 - 备份文件: {summary['total_backups']}, "
                       f"有效: {summary['valid_backups']}, "
                       f"告警: {summary['total_alerts']}")
            
            return len(self.status['alerts']) == 0
            
        except Exception as e:
            logger.error(f"监控执行失败: {e}")
            return False

def main():
    """主函数"""
    monitor = BackupMonitor()
    success = monitor.run()
    sys.exit(0 if success else 1)

if __name__ == '__main__':
    main()